<!DOCTYPE html>
<!-- saved from url=(0047)http://javascript.ruanyifeng.com/dom/event.html -->
<html class="no-js" lang="en"><!--<![endif]--><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  

  <!-- Set the viewport width to device width for mobile -->
  <meta name="viewport" content="width=device-width">

  <title>事件模型 -- JavaScript 标准参考教程（alpha）</title>
  
  <!-- Included CSS Files (Uncompressed) -->
  <!--
  <link rel="stylesheet" href="stylesheets/foundation.css">
  -->
  
  <!-- Included CSS Files (Compressed) -->
  <link rel="stylesheet" href="./事件模型 -- JavaScript 标准参考教程（alpha）_files/foundation.css">
  <link rel="stylesheet" href="./事件模型 -- JavaScript 标准参考教程（alpha）_files/main.css">

  <!-- IE Fix for HTML5 Tags -->
  <!--[if lt IE 9]>
    <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->

  <script async="" src="http://www.google-analytics.com/analytics.js"></script><script src="./事件模型 -- JavaScript 标准参考教程（alpha）_files/jquery.js"></script><style type="text/css">
:root #homepage_pos,
:root #gd1
{ display: none !important; }</style>
  <script src="./事件模型 -- JavaScript 标准参考教程（alpha）_files/toc.js"></script>
  <script src="./事件模型 -- JavaScript 标准参考教程（alpha）_files/main.js"></script>

<script type="text/javascript" async="" src="http://jstutorial.disqus.com/embed.js"></script><style type="text/css">
:root #header + #content > #left > #rlblock_left
{ display: none !important; }</style></head>
<body>

<header class="top-bar" id="header">

<div class="fixed">

<nav class="top-bar">
<ul>
<!-- Title Area -->
	<li class="name has-dropdown">
	<h1><a href="http://javascript.ruanyifeng.com/">JavaScript 标准参考教程（alpha） </a></h1>
		<ul class="dropdown">
			
			<li><a href="http://javascript.ruanyifeng.com/#introduction">导论</a></li>
			
			<li><a href="http://javascript.ruanyifeng.com/#grammar">语法</a></li>
			
			<li><a href="http://javascript.ruanyifeng.com/#stdlib">标准库</a></li>
			
			<li><a href="http://javascript.ruanyifeng.com/#oop">面向对象编程</a></li>
			
			<li><a href="http://javascript.ruanyifeng.com/#advanced">语法专题</a></li>
			
			<li><a href="http://javascript.ruanyifeng.com/#dom">DOM模型</a></li>
			
			<li><a href="http://javascript.ruanyifeng.com/#bom">浏览器环境</a></li>
			
			<li><a href="http://javascript.ruanyifeng.com/#htmlapi">Web API</a></li>
			
		</ul>
	</li>
</ul>

<section>


<ul class="left">
<li class="divider"></li>
<li class="has-dropdown"><a class="active" href="http://javascript.ruanyifeng.com/dom/event.html#"> DOM模型 </a><ul class="dropdown">















<li><a href="http://javascript.ruanyifeng.com/dom/attribute.html">属性的操作</a></li>





































<li><a href="http://javascript.ruanyifeng.com/dom/css.html">CSS操作</a></li>



















<li><a href="http://javascript.ruanyifeng.com/dom/document.html">document节点</a></li>







<li><a href="http://javascript.ruanyifeng.com/dom/element.html">Element对象</a></li>











<li><a href="http://javascript.ruanyifeng.com/dom/event-type.html">事件种类</a></li>





<li class="active"><a href="http://javascript.ruanyifeng.com/dom/event.html#">事件模型</a></li>

































<li><a href="http://javascript.ruanyifeng.com/dom/image.html">Image对象</a></li>



































<li><a href="http://javascript.ruanyifeng.com/dom/mutationobserver.html">Mutation Observer API</a></li>







<li><a href="http://javascript.ruanyifeng.com/dom/node.html">DOM 模型概述</a></li>















































































<li><a href="http://javascript.ruanyifeng.com/dom/text.html">Text节点和DocumentFragment节点</a></li>













































</ul></li>
<li class="divider"></li>
<li class="has-dropdown nav-3"><a href="http://javascript.ruanyifeng.com/dom/event.html#"> 事件模型</a><ul class="dropdown">
<li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc0">EventTarget接口</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc4">监听函数</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc9">事件的传播</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc12">Event对象</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc20">自定义事件和事件模拟</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc25">参考链接</a></li></ul></li>
</ul>


<ul class="right">
	<li class="divider"></li>
	<li>
		<a href="https://github.com/ruanyf/jstutorial" target="_blank">GitHub <i class="foundicon-edit"></i></a>
	</li>
	<li class="divider"></li>
	<li>
		<a href="http://javascript.ruanyifeng.com/dom/event.html#">TOP <i class="foundicon-up-arrow"></i></a>
	</li>
</ul>

</section>

</nav>  
</div>
</header>


<article class="bookPage">

  <div class="row">
    <div class="twelve columns">

<h1> 事件模型 </h1>

<aside class="right"><p>来自<a href="http://javascript.ruanyifeng.com/">《JavaScript 标准参考教程（alpha）》</a>，by 阮一峰</p></aside>

<h2>目录</h2><div id="toc" class="panel callout radius"><ul><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc0">EventTarget接口</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc1">addEventListener()</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc2">removeEventListener()</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc3">dispatchEvent()</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc4">监听函数</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc5">HTML标签的on-属性</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc6">Element节点的事件属性</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc7">addEventListener方法</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc8">this对象的指向</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc9">事件的传播</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc10">传播的三个阶段</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc11">事件的代理</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc12">Event对象</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc13">event.bubbles，event.eventPhase</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc14">event.cancelable，event.defaultPrevented</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc15">event.currentTarget，event.target</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc16">event.type，event.detail，event.timeStamp，event.isTrusted</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc17">event.preventDefault()</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc18">event.stopPropagation()</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc19">event.stopImmediatePropagation()</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc20">自定义事件和事件模拟</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc21">CustomEvent()</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc22">事件的模拟</a></li><li class="toc-h3"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc23">自定义事件的老式写法</a></li><li class="toc-h3 toc-active"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc24">事件模拟的老式写法</a></li><li class="toc-h2"><a href="http://javascript.ruanyifeng.com/dom/event.html#toc25">参考链接</a></li></ul></div>


<p>事件是一种异步编程的实现方式，本质上是程序各个组成部分之间的通信。DOM支持大量的事件，本节介绍DOM的事件编程。</p>

<span id="toc0"></span><div class="chapter"><h2 id="eventtarget接口">EventTarget接口</h2></div>

<p>DOM的事件操作（监听和触发），都定义在<code class="highlighter-rouge">EventTarget</code>接口。<code class="highlighter-rouge">Element</code>节点、<code class="highlighter-rouge">document</code>节点和<code class="highlighter-rouge">window</code>对象，都部署了这个接口。此外，<code class="highlighter-rouge">XMLHttpRequest</code>、<code class="highlighter-rouge">AudioNode</code>、<code class="highlighter-rouge">AudioContext</code>等浏览器内置对象，也部署了这个接口。</p>

<p>该接口就是三个方法。</p>

<ul>
  <li><code class="highlighter-rouge">addEventListener</code>：绑定事件的监听函数</li>
  <li><code class="highlighter-rouge">removeEventListener</code>：移除事件的监听函数</li>
  <li><code class="highlighter-rouge">dispatchEvent</code>：触发事件</li>
</ul>

<span id="toc1"></span><h3 id="addeventlistener">addEventListener()</h3>

<p><code class="highlighter-rouge">addEventListener</code>方法用于在当前节点或对象上，定义一个特定事件的监听函数。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="c1">// 使用格式</span>
<span class="nx">target</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="nx">type</span><span class="p">,</span> <span class="nx">listener</span><span class="p">[,</span> <span class="nx">useCapture</span><span class="p">]);</span>

<span class="c1">// 实例</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'load'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{...},</span> <span class="kc">false</span><span class="p">);</span>
<span class="nx">request</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'readystatechange'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{...},</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p><code class="highlighter-rouge">addEventListener</code>方法接受三个参数。</p>

<ul>
  <li><code class="highlighter-rouge">type</code>：事件名称，大小写敏感。</li>
  <li><code class="highlighter-rouge">listener</code>：监听函数。事件发生时，会调用该监听函数。</li>
  <li><code class="highlighter-rouge">useCapture</code>：布尔值，表示监听函数是否在捕获阶段（capture）触发（参见后文《事件的传播》部分），默认为<code class="highlighter-rouge">false</code>（监听函数只在冒泡阶段被触发）。老式浏览器规定该参数必写，较新版本的浏览器允许该参数可选。为了保持兼容，建议总是写上该参数。</li>
</ul>

<p>下面是一个例子。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hello</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Hello world'</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">var</span> <span class="nx">button</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'btn'</span><span class="p">);</span>
<span class="nx">button</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">hello</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p>上面代码中，<code class="highlighter-rouge">addEventListener</code>方法为<code class="highlighter-rouge">button</code>元素节点，绑定<code class="highlighter-rouge">click</code>事件的监听函数<code class="highlighter-rouge">hello</code>，该函数只在冒泡阶段触发。</p>

<p><code class="highlighter-rouge">addEventListener</code>方法可以为当前对象的同一个事件，添加多个监听函数。这些函数按照添加顺序触发，即先添加先触发。如果为同一个事件多次添加同一个监听函数，该函数只会执行一次，多余的添加将自动被去除（不必使用<code class="highlighter-rouge">removeEventListener</code>方法手动去除）。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hello</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Hello world'</span><span class="p">);</span>
<span class="p">}</span>

<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">hello</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">hello</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p>执行上面代码，点击文档只会输出一行<code class="highlighter-rouge">Hello world</code>。</p>

<p>如果希望向监听函数传递参数，可以用匿名函数包装一下监听函数。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">print</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">x</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">var</span> <span class="nx">el</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'div1'</span><span class="p">);</span>
<span class="nx">el</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="nx">print</span><span class="p">(</span><span class="s1">'Hello'</span><span class="p">);</span> <span class="p">},</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p>上面代码通过匿名函数，向监听函数<code class="highlighter-rouge">print</code>传递了一个参数。</p>

<span id="toc2"></span><h3 id="removeeventlistener">removeEventListener()</h3>

<p><code class="highlighter-rouge">removeEventListener</code>方法用来移除<code class="highlighter-rouge">addEventListener</code>方法添加的事件监听函数。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">div</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">listener</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
<span class="nx">div</span><span class="p">.</span><span class="nx">removeEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">listener</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p><code class="highlighter-rouge">removeEventListener</code>方法的参数，与<code class="highlighter-rouge">addEventListener</code>方法完全一致。它的第一个参数“事件类型”，大小写敏感。</p>

<p>注意，<code class="highlighter-rouge">removeEventListener</code>方法移除的监听函数，必须与对应的<code class="highlighter-rouge">addEventListener</code>方法的参数完全一致，而且必须在同一个元素节点，否则无效。</p>

<span id="toc3"></span><h3 id="dispatchevent">dispatchEvent()</h3>

<p><code class="highlighter-rouge">dispatchEvent</code>方法在当前节点上触发指定事件，从而触发监听函数的执行。该方法返回一个布尔值，只要有一个监听函数调用了<code class="highlighter-rouge">Event.preventDefault()</code>，则返回值为<code class="highlighter-rouge">false</code>，否则为<code class="highlighter-rouge">true</code>。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">target</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span>
</code></pre>
</div>

<p><code class="highlighter-rouge">dispatchEvent</code>方法的参数是一个<code class="highlighter-rouge">Event</code>对象的实例。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">para</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">hello</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">event</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Event</span><span class="p">(</span><span class="s1">'click'</span><span class="p">);</span>
<span class="nx">para</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
</code></pre>
</div>

<p>上面代码在当前节点触发了<code class="highlighter-rouge">click</code>事件。</p>

<p>如果<code class="highlighter-rouge">dispatchEvent</code>方法的参数为空，或者不是一个有效的事件对象，将报错。</p>

<p>下面代码根据<code class="highlighter-rouge">dispatchEvent</code>方法的返回值，判断事件是否被取消了。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">canceled</span> <span class="o">=</span> <span class="o">!</span><span class="nx">cb</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">canceled</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'事件取消'</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'事件未取消'</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre>
</div>

<span id="toc4"></span><div class="chapter"><h2 id="监听函数">监听函数</h2></div>

<p>监听函数（listener）是事件发生时，程序所要执行的函数。它是事件驱动编程模式的主要编程方式。</p>

<p>DOM提供三种方法，可以用来为事件绑定监听函数。</p>

<span id="toc5"></span><h3 id="html标签的on-属性">HTML标签的on-属性</h3>

<p>HTML语言允许在元素标签的属性中，直接定义某些事件的监听代码。</p>

<div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;body</span> <span class="na">onload=</span><span class="s">"doSomething()"</span><span class="nt">&gt;</span>
<span class="nt">&lt;div</span> <span class="na">onclick=</span><span class="s">"console.log('触发事件')"</span><span class="nt">&gt;</span>
</code></pre>
</div>

<p>上面代码为<code class="highlighter-rouge">body</code>节点的<code class="highlighter-rouge">load</code>事件、<code class="highlighter-rouge">div</code>节点的<code class="highlighter-rouge">click</code>事件，指定了监听函数。</p>

<p>使用这个方法指定的监听函数，只会在冒泡阶段触发。</p>

<p>注意，使用这种方法时，<code class="highlighter-rouge">on-</code>属性的值是将会执行的代码，而不是一个函数。</p>

<div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="c">&lt;!-- 正确 --&gt;</span>
<span class="nt">&lt;body</span> <span class="na">onload=</span><span class="s">"doSomething()"</span><span class="nt">&gt;</span>

<span class="c">&lt;!-- 错误 --&gt;</span>
<span class="nt">&lt;body</span> <span class="na">onload=</span><span class="s">"doSomething"</span><span class="nt">&gt;</span>
</code></pre>
</div>

<p>一旦指定的事件发生，<code class="highlighter-rouge">on-</code>属性的值是原样传入JavaScript引擎执行。因此如果要执行函数，不要忘记加上一对圆括号。</p>

<p>另外，Element元素节点的<code class="highlighter-rouge">setAttribute</code>方法，其实设置的也是这种效果。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">el</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'onclick'</span><span class="p">,</span> <span class="s1">'doSomething()'</span><span class="p">);</span>
</code></pre>
</div>

<span id="toc6"></span><h3 id="element节点的事件属性">Element节点的事件属性</h3>

<p>Element节点对象有事件属性，同样可以指定监听函数。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">doSomething</span><span class="p">;</span>

<span class="nx">div</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">){</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'触发事件'</span><span class="p">);</span>
<span class="p">};</span>
</code></pre>
</div>

<p>使用这个方法指定的监听函数，只会在冒泡阶段触发。</p>

<span id="toc7"></span><h3 id="addeventlistener方法">addEventListener方法</h3>

<p>通过<code class="highlighter-rouge">Element</code>节点、<code class="highlighter-rouge">document</code>节点、<code class="highlighter-rouge">window</code>对象的<code class="highlighter-rouge">addEventListener</code>方法，也可以定义事件的监听函数。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'load'</span><span class="p">,</span> <span class="nx">doSomething</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p>addEventListener方法的详细介绍，参见本节EventTarget接口的部分。</p>

<p>在上面三种方法中，第一种“HTML标签的on-属性”，违反了HTML与JavaScript代码相分离的原则；第二种“Element节点的事件属性”的缺点是，同一个事件只能定义一个监听函数，也就是说，如果定义两次onclick属性，后一次定义会覆盖前一次。因此，这两种方法都不推荐使用，除非是为了程序的兼容问题，因为所有浏览器都支持这两种方法。</p>

<p>addEventListener是推荐的指定监听函数的方法。它有如下优点：</p>

<ul>
  <li>
    <p>可以针对同一个事件，添加多个监听函数。</p>
  </li>
  <li>
    <p>能够指定在哪个阶段（捕获阶段还是冒泡阶段）触发回监听函数。</p>
  </li>
  <li>
    <p>除了DOM节点，还可以部署在<code class="highlighter-rouge">window</code>、<code class="highlighter-rouge">XMLHttpRequest</code>等对象上面，等于统一了整个JavaScript的监听函数接口。</p>
  </li>
</ul>

<span id="toc8"></span><h3 id="this对象的指向">this对象的指向</h3>

<p>实际编程中，监听函数内部的<code class="highlighter-rouge">this</code>对象，常常需要指向触发事件的那个Element节点。</p>

<p><code class="highlighter-rouge">addEventListener</code>方法指定的监听函数，内部的<code class="highlighter-rouge">this</code>对象总是指向触发事件的那个节点。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="c1">// HTML代码为</span>
<span class="c1">// &lt;p id="para"&gt;Hello&lt;/p&gt;</span>

<span class="kd">var</span> <span class="nx">id</span> <span class="o">=</span> <span class="s1">'doc'</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">para</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'para'</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">hello</span><span class="p">(){</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">id</span><span class="p">);</span>
<span class="p">}</span>

<span class="nx">para</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">hello</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p>执行上面代码，点击<code class="highlighter-rouge">&lt;p&gt;</code>节点会输出<code class="highlighter-rouge">para</code>。这是因为监听函数被“拷贝”成了节点的一个属性，所以<code class="highlighter-rouge">this</code>指向节点对象。使用下面的写法，会看得更清楚。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">para</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="nx">hello</span><span class="p">;</span>
</code></pre>
</div>

<p>如果将监听函数部署在Element节点的<code class="highlighter-rouge">on-</code>属性上面，<code class="highlighter-rouge">this</code>不会指向触发事件的元素节点。</p>

<div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;p</span> <span class="na">id=</span><span class="s">"para"</span> <span class="na">onclick=</span><span class="s">"hello()"</span><span class="nt">&gt;</span>Hello<span class="nt">&lt;/p&gt;</span>
<span class="c">&lt;!-- 或者使用JavaScript代码  --&gt;</span>
<span class="nt">&lt;script&gt;</span>
  <span class="nx">pElement</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'onclick'</span><span class="p">,</span> <span class="s1">'hello()'</span><span class="p">);</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre>
</div>

<p>执行上面代码，点击<code class="highlighter-rouge">&lt;p&gt;</code>节点会输出<code class="highlighter-rouge">doc</code>。这是因为这里只是调用<code class="highlighter-rouge">hello</code>函数，而<code class="highlighter-rouge">hello</code>函数实际是在全局作用域执行，相当于下面的代码。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">para</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
  <span class="nx">hello</span><span class="p">();</span>
<span class="p">}</span>
</code></pre>
</div>

<p>一种解决方法是，不引入函数作用域，直接在<code class="highlighter-rouge">on-</code>属性写入所要执行的代码。因为<code class="highlighter-rouge">on-</code>属性是在当前节点上执行的。</p>

<div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;p</span> <span class="na">id=</span><span class="s">"para"</span> <span class="na">onclick=</span><span class="s">"console.log(id)"</span><span class="nt">&gt;</span>Hello<span class="nt">&lt;/p&gt;</span>
<span class="c">&lt;!-- 或者 --&gt;</span>
<span class="nt">&lt;p</span> <span class="na">id=</span><span class="s">"para"</span> <span class="na">onclick=</span><span class="s">"console.log(this.id)"</span><span class="nt">&gt;</span>Hello<span class="nt">&lt;/p&gt;</span>
</code></pre>
</div>

<p>上面两行，最后输出的都是<code class="highlighter-rouge">para</code>。</p>

<p>总结一下，以下写法的<code class="highlighter-rouge">this</code>对象都指向Element节点。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="c1">// JavaScript代码</span>
<span class="nx">element</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="nx">print</span>
<span class="nx">element</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">print</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
<span class="nx">element</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">id</span><span class="p">);}</span>

<span class="c1">// HTML代码</span>
<span class="o">&lt;</span><span class="nx">element</span> <span class="nx">onclick</span><span class="o">=</span><span class="s2">"console.log(this.id)"</span><span class="o">&gt;</span>
</code></pre>
</div>

<p>以下写法的<code class="highlighter-rouge">this</code>对象，都指向全局对象。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="c1">// JavaScript代码</span>
<span class="nx">element</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(){</span> <span class="nx">doSomething</span><span class="p">()</span> <span class="p">};</span>
<span class="nx">element</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'onclick'</span><span class="p">,</span> <span class="s1">'doSomething()'</span><span class="p">);</span>

<span class="c1">// HTML代码</span>
<span class="o">&lt;</span><span class="nx">element</span> <span class="nx">onclick</span><span class="o">=</span><span class="s2">"doSomething()"</span><span class="o">&gt;</span>
</code></pre>
</div>

<span id="toc9"></span><div class="chapter"><h2 id="事件的传播">事件的传播</h2></div>

<span id="toc10"></span><h3 id="传播的三个阶段">传播的三个阶段</h3>

<p>当一个事件发生以后，它会在不同的DOM节点之间传播（propagation）。这种传播分成三个阶段：</p>

<ul>
  <li>
    <p><strong>第一阶段</strong>：从window对象传导到目标节点，称为“捕获阶段”（capture phase）。</p>
  </li>
  <li>
    <p><strong>第二阶段</strong>：在目标节点上触发，称为“目标阶段”（target phase）。</p>
  </li>
  <li>
    <p><strong>第三阶段</strong>：从目标节点传导回window对象，称为“冒泡阶段”（bubbling phase）。</p>
  </li>
</ul>

<p>这种三阶段的传播模型，会使得一个事件在多个节点上触发。比如，假设点击<code class="highlighter-rouge">&lt;div&gt;</code>之中嵌套一个<code class="highlighter-rouge">&lt;p&gt;</code>节点。</p>

<div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;div&gt;</span>
  <span class="nt">&lt;p&gt;</span>Click Me<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre>
</div>

<p>如果对这两个节点的<code class="highlighter-rouge">click</code>事件都设定监听函数，则<code class="highlighter-rouge">click</code>事件会被触发四次。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">phases</span> <span class="o">=</span> <span class="p">{</span>
  <span class="mi">1</span><span class="p">:</span> <span class="s1">'capture'</span><span class="p">,</span>
  <span class="mi">2</span><span class="p">:</span> <span class="s1">'target'</span><span class="p">,</span>
  <span class="mi">3</span><span class="p">:</span> <span class="s1">'bubble'</span>
<span class="p">};</span>

<span class="kd">var</span> <span class="nx">div</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">'div'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">p</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">'p'</span><span class="p">);</span>

<span class="nx">div</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">callback</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="nx">p</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">callback</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="nx">div</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">callback</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
<span class="nx">p</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">callback</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">tag</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">currentTarget</span><span class="p">.</span><span class="nx">tagName</span><span class="p">;</span>
  <span class="kd">var</span> <span class="nx">phase</span> <span class="o">=</span> <span class="nx">phases</span><span class="p">[</span><span class="nx">event</span><span class="p">.</span><span class="nx">eventPhase</span><span class="p">];</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"Tag: '"</span> <span class="o">+</span> <span class="nx">tag</span> <span class="o">+</span> <span class="s2">"'. EventPhase: '"</span> <span class="o">+</span> <span class="nx">phase</span> <span class="o">+</span> <span class="s2">"'"</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// 点击以后的结果</span>
<span class="c1">// Tag: 'DIV'. EventPhase: 'capture'</span>
<span class="c1">// Tag: 'P'. EventPhase: 'target'</span>
<span class="c1">// Tag: 'P'. EventPhase: 'target'</span>
<span class="c1">// Tag: 'DIV'. EventPhase: 'bubble'</span>
</code></pre>
</div>

<p>上面代码表示，<code class="highlighter-rouge">click</code>事件被触发了四次：<code class="highlighter-rouge">&lt;p&gt;</code>节点的捕获阶段和冒泡阶段各1次，<code class="highlighter-rouge">&lt;div&gt;</code>节点的捕获阶段和冒泡阶段各1次。</p>

<ol>
  <li>捕获阶段：事件从<code class="highlighter-rouge">&lt;div&gt;</code>向<code class="highlighter-rouge">&lt;p&gt;</code>传播时，触发<code class="highlighter-rouge">&lt;div&gt;</code>的<code class="highlighter-rouge">click</code>事件；</li>
  <li>目标阶段：事件从<code class="highlighter-rouge">&lt;div&gt;</code>到达<code class="highlighter-rouge">&lt;p&gt;</code>时，触发<code class="highlighter-rouge">&lt;p&gt;</code>的<code class="highlighter-rouge">click</code>事件；</li>
  <li>目标阶段：事件离开<code class="highlighter-rouge">&lt;p&gt;</code>时，触发<code class="highlighter-rouge">&lt;p&gt;</code>的<code class="highlighter-rouge">click</code>事件；</li>
  <li>冒泡阶段：事件从<code class="highlighter-rouge">&lt;p&gt;</code>传回<code class="highlighter-rouge">&lt;div&gt;</code>时，再次触发<code class="highlighter-rouge">&lt;div&gt;</code>的<code class="highlighter-rouge">click</code>事件。</li>
</ol>

<p>注意，用户点击网页的时候，浏览器总是假定<code class="highlighter-rouge">click</code>事件的目标节点，就是点击位置的嵌套最深的那个节点（嵌套在<code class="highlighter-rouge">&lt;div&gt;</code>节点的<code class="highlighter-rouge">&lt;p&gt;</code>节点）。所以，<code class="highlighter-rouge">&lt;p&gt;</code>节点的捕获阶段和冒泡阶段，都会显示为<code class="highlighter-rouge">target</code>阶段。</p>

<p>事件传播的最上层对象是<code class="highlighter-rouge">window</code>，接着依次是<code class="highlighter-rouge">document</code>，<code class="highlighter-rouge">html</code>（<code class="highlighter-rouge">document.documentElement</code>）和<code class="highlighter-rouge">body</code>（<code class="highlighter-rouge">document.dody</code>）。也就是说，如果<code class="highlighter-rouge">&lt;body&gt;</code>元素中有一个<code class="highlighter-rouge">&lt;div&gt;</code>元素，点击该元素。事件的传播顺序，在捕获阶段依次为<code class="highlighter-rouge">window</code>、<code class="highlighter-rouge">document</code>、<code class="highlighter-rouge">html</code>、<code class="highlighter-rouge">body</code>、<code class="highlighter-rouge">div</code>，在冒泡阶段依次为<code class="highlighter-rouge">div</code>、<code class="highlighter-rouge">body</code>、<code class="highlighter-rouge">html</code>、<code class="highlighter-rouge">document</code>、<code class="highlighter-rouge">window</code>。</p>

<span id="toc11"></span><h3 id="事件的代理">事件的代理</h3>

<p>由于事件会在冒泡阶段向上传播到父节点，因此可以把子节点的监听函数定义在父节点上，由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理（delegation）。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">ul</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">'ul'</span><span class="p">);</span>

<span class="nx">ul</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">tagName</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()</span> <span class="o">===</span> <span class="s1">'li'</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// some code</span>
  <span class="p">}</span>
<span class="p">});</span>
</code></pre>
</div>

<p>上面代码的<code class="highlighter-rouge">click</code>事件的监听函数定义在<code class="highlighter-rouge">&lt;ul&gt;</code>节点，但是实际上，它处理的是子节点<code class="highlighter-rouge">&lt;li&gt;</code>的<code class="highlighter-rouge">click</code>事件。这样做的好处是，只要定义一个监听函数，就能处理多个子节点的事件，而且以后再添加子节点，监听函数依然有效。</p>

<p>如果希望事件到某个节点为止，不再传播，可以使用事件对象的<code class="highlighter-rouge">stopPropagation</code>方法。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">p</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">event</span><span class="p">.</span><span class="nx">stopPropagation</span><span class="p">();</span>
<span class="p">});</span>
</code></pre>
</div>

<p>使用上面的代码以后，<code class="highlighter-rouge">click</code>事件在冒泡阶段到达<code class="highlighter-rouge">&lt;p&gt;</code>节点以后，就不再向上（父节点的方向）传播了。</p>

<p>但是，<code class="highlighter-rouge">stopPropagation</code>方法只会阻止当前监听函数的传播，不会阻止<code class="highlighter-rouge">&lt;p&gt;</code>节点上的其他<code class="highlighter-rouge">click</code>事件的监听函数。如果想要不再触发那些监听函数，可以使用<code class="highlighter-rouge">stopImmediatePropagation</code>方法。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">p</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
 <span class="nx">event</span><span class="p">.</span><span class="nx">stopImmediatePropagation</span><span class="p">();</span>
<span class="p">});</span>

<span class="nx">p</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
 <span class="c1">// 不会被触发</span>
<span class="p">});</span>
</code></pre>
</div>

<span id="toc12"></span><div class="chapter"><h2 id="event对象">Event对象</h2></div>

<p>事件发生以后，会生成一个事件对象，作为参数传给监听函数。浏览器原生提供一个<code class="highlighter-rouge">Event</code>对象，所有的事件都是这个对象的实例，或者说继承了<code class="highlighter-rouge">Event.prototype</code>对象。</p>

<p><code class="highlighter-rouge">Event</code>对象本身就是一个构造函数，可以用来生成新的实例。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">event</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Event</span><span class="p">(</span><span class="nx">typeArg</span><span class="p">,</span> <span class="nx">eventInit</span><span class="p">);</span>
</code></pre>
</div>

<p>Event构造函数接受两个参数。第一个参数是字符串，表示事件的名称；第二个参数是一个对象，表示事件对象的配置。该参数可以有以下两个属性。</p>

<ul>
  <li><code class="highlighter-rouge">bubbles</code>：布尔值，可选，默认为<code class="highlighter-rouge">false</code>，表示事件对象是否冒泡。</li>
  <li><code class="highlighter-rouge">cancelable</code>：布尔值，可选，默认为<code class="highlighter-rouge">false</code>，表示事件是否可以被取消。</li>
</ul>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">ev</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Event</span><span class="p">(</span>
  <span class="s1">'look'</span><span class="p">,</span>
  <span class="p">{</span>
    <span class="s1">'bubbles'</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="s1">'cancelable'</span><span class="p">:</span> <span class="kc">false</span>
  <span class="p">}</span>
<span class="p">);</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">ev</span><span class="p">);</span>
</code></pre>
</div>

<p>上面代码新建一个<code class="highlighter-rouge">look</code>事件实例，然后使用<code class="highlighter-rouge">dispatchEvent</code>方法触发该事件。</p>

<p>IE8及以下版本，事件对象不作为参数传递，而是通过<code class="highlighter-rouge">window</code>对象的<code class="highlighter-rouge">event</code>属性读取，并且事件对象的<code class="highlighter-rouge">target</code>属性叫做<code class="highlighter-rouge">srcElement</code>属性。所以，以前获取事件信息，往往要写成下面这样。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">myEventHandler</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">actualEvent</span> <span class="o">=</span> <span class="nx">event</span> <span class="o">||</span> <span class="nb">window</span><span class="p">.</span><span class="nx">event</span><span class="p">;</span>
  <span class="kd">var</span> <span class="nx">actualTarget</span> <span class="o">=</span> <span class="nx">actualEvent</span><span class="p">.</span><span class="nx">target</span> <span class="o">||</span> <span class="nx">actualEvent</span><span class="p">.</span><span class="nx">srcElement</span><span class="p">;</span>
  <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre>
</div>

<p>上面的代码只是为了说明以前的程序为什么这样写，在新代码中，这样的写法不应该再用了。</p>

<span id="toc13"></span><h3 id="eventbubbleseventeventphase">event.bubbles，event.eventPhase</h3>

<p>以下属性与事件的阶段有关。</p>

<p><strong>（1）bubbles</strong></p>

<p>bubbles属性返回一个布尔值，表示当前事件是否会冒泡。该属性为只读属性，只能在新建事件时改变。除非显式声明，Event构造函数生成的事件，默认是不冒泡的。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">goInput</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">e</span><span class="p">.</span><span class="nx">bubbles</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">passItOn</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">doOutput</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre>
</div>

<p>上面代码根据事件是否冒泡，调用不同的函数。</p>

<p><strong>（2）event.eventPhase</strong></p>

<p>eventPhase属性返回一个整数值，表示事件目前所处的节点。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">phase</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">eventPhase</span><span class="p">;</span>
</code></pre>
</div>

<ul>
  <li>0，事件目前没有发生。</li>
  <li>1，事件目前处于捕获阶段，即处于从祖先节点向目标节点的传播过程中。该过程是从Window对象到Document节点，再到HTMLHtmlElement节点，直到目标节点的父节点为止。</li>
  <li>2，事件到达目标节点，即target属性指向的那个节点。</li>
  <li>3，事件处于冒泡阶段，即处于从目标节点向祖先节点的反向传播过程中。该过程是从父节点一直到Window对象。只有bubbles属性为true时，这个阶段才可能发生。</li>
</ul>

<span id="toc14"></span><h3 id="eventcancelableeventdefaultprevented">event.cancelable，event.defaultPrevented</h3>

<p>以下属性与事件的默认行为有关。</p>

<p><strong>（1）cancelable</strong></p>

<p>cancelable属性返回一个布尔值，表示事件是否可以取消。该属性为只读属性，只能在新建事件时改变。除非显式声明，Event构造函数生成的事件，默认是不可以取消的。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">bool</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">cancelable</span><span class="p">;</span>
</code></pre>
</div>

<p>如果要取消某个事件，需要在这个事件上面调用preventDefault方法，这会阻止浏览器对某种事件部署的默认行为。</p>

<p><strong>（2）defaultPrevented</strong></p>

<p>defaultPrevented属性返回一个布尔值，表示该事件是否调用过preventDefault方法。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">defaultPrevented</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre>
</div>

<span id="toc15"></span><h3 id="eventcurrenttargeteventtarget">event.currentTarget，event.target</h3>

<p>以下属性与事件的目标节点有关。</p>

<p><strong>（1）currentTarget</strong></p>

<p>currentTarget属性返回事件当前所在的节点，即正在执行的监听函数所绑定的那个节点。作为比较，target属性返回事件发生的节点。如果监听函数在捕获阶段和冒泡阶段触发，那么这两个属性返回的值是不一样的。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hide</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span> <span class="o">===</span> <span class="nx">e</span><span class="p">.</span><span class="nx">currentTarget</span><span class="p">);</span>  <span class="c1">// true</span>
  <span class="nx">e</span><span class="p">.</span><span class="nx">currentTarget</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">visibility</span> <span class="o">=</span> <span class="s2">"hidden"</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">para</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">hide</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p>上面代码中，点击para节点，该节点会不可见。另外，在监听函数中，currentTarget属性实际上等同于this对象。</p>

<p><strong>（2）target</strong></p>

<p>target属性返回触发事件的那个节点，即事件最初发生的节点。如果监听函数不在该节点触发，那么它与currentTarget属性返回的值是不一样的。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hide</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span> <span class="o">===</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">);</span>  <span class="c1">// 有可能不是true</span>
  <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">visibility</span> <span class="o">=</span> <span class="s2">"hidden"</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// HTML代码为</span>
<span class="c1">// &lt;p id="para"&gt;Hello &lt;em&gt;World&lt;/em&gt;&lt;/p&gt;</span>
<span class="nx">para</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">hide</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p>上面代码中，如果在para节点的em子节点上面点击，则<code class="highlighter-rouge">e.target</code>指向em子节点，导致em子节点（即World部分）会不可见，且输出false。</p>

<p>在IE6—IE8之中，该属性的名字不是target，而是srcElement，因此经常可以看到下面这样的代码。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hide</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span> <span class="o">||</span> <span class="nx">e</span><span class="p">.</span><span class="nx">srcElement</span><span class="p">;</span>
  <span class="nx">target</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">visibility</span> <span class="o">=</span> <span class="s1">'hidden'</span><span class="p">;</span>
<span class="p">}</span>
</code></pre>
</div>

<span id="toc16"></span><h3 id="eventtypeeventdetaileventtimestampeventistrusted">event.type，event.detail，event.timeStamp，event.isTrusted</h3>

<p>以下属性与事件对象的其他信息相关。</p>

<p><strong>（1）type</strong></p>

<p><code class="highlighter-rouge">type</code>属性返回一个字符串，表示事件类型，大小写敏感。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">string</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
</code></pre>
</div>

<p><strong>（2）detail</strong></p>

<p><code class="highlighter-rouge">detail</code>属性返回一个数值，表示事件的某种信息。具体含义与事件类型有关，对于鼠标事件，表示鼠标按键在某个位置按下的次数，比如对于dblclick事件，detail属性的值总是2。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">giveDetails</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">detail</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">el</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="nx">giveDetails</span><span class="p">;</span>
</code></pre>
</div>

<p><strong>（3）timeStamp</strong></p>

<p><code class="highlighter-rouge">timeStamp</code>属性返回一个毫秒时间戳，表示事件发生的时间。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">number</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">timeStamp</span><span class="p">;</span>
</code></pre>
</div>

<p>Chrome在49版以前，这个属性返回的是一个整数，单位是毫秒（millisecond），表示从Unix纪元开始的时间戳。从49版开始，该属性返回的是一个高精度时间戳，也就是说，毫秒之后还带三位小数，精确到微秒。并且，这个值不再从Unix纪元开始计算，而是从<code class="highlighter-rouge">PerformanceTiming.navigationStart</code>开始计算，即表示距离用户导航至该网页的时间。如果想将这个值转为Unix纪元时间戳，就要计算<code class="highlighter-rouge">event.timeStamp + performance.timing.navigationStart</code>。</p>

<p>下面是一个计算鼠标移动速度的例子，显示每秒移动的像素数量。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">previousX</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">previousY</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">previousT</span><span class="p">;</span>

<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'mousemove'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="nx">previousX</span> <span class="o">===</span> <span class="kc">undefined</span> <span class="o">||</span>
        <span class="nx">previousY</span> <span class="o">===</span> <span class="kc">undefined</span> <span class="o">||</span>
        <span class="nx">previousT</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">))</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">deltaX</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">screenX</span> <span class="o">-</span> <span class="nx">previousX</span><span class="p">;</span>
    <span class="kd">var</span> <span class="nx">deltaY</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">screenY</span> <span class="o">-</span> <span class="nx">previousY</span><span class="p">;</span>
    <span class="kd">var</span> <span class="nx">deltaD</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">sqrt</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">pow</span><span class="p">(</span><span class="nx">deltaX</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">pow</span><span class="p">(</span><span class="nx">deltaY</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>

    <span class="kd">var</span> <span class="nx">deltaT</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">timeStamp</span> <span class="o">-</span> <span class="nx">previousT</span><span class="p">;</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">deltaD</span> <span class="o">/</span> <span class="nx">deltaT</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nx">previousX</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">screenX</span><span class="p">;</span>
  <span class="nx">previousY</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">screenY</span><span class="p">;</span>
  <span class="nx">previousT</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">timeStamp</span><span class="p">;</span>
<span class="p">});</span>
</code></pre>
</div>

<p><strong>（4）isTrusted</strong></p>

<p><code class="highlighter-rouge">isTrusted</code>属性返回一个布尔值，表示该事件是否为真实用户触发。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">bool</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">isTrusted</span><span class="p">;</span>
</code></pre>
</div>

<p>用户触发的事件返回<code class="highlighter-rouge">true</code>，脚本触发的事件返回<code class="highlighter-rouge">false</code>。</p>

<span id="toc17"></span><h3 id="eventpreventdefault">event.preventDefault()</h3>

<p>preventDefault方法取消浏览器对当前事件的默认行为，比如点击链接后，浏览器跳转到指定页面，或者按一下空格键，页面向下滚动一段距离。该方法生效的前提是，事件的cancelable属性为true，如果为false，则调用该方法没有任何效果。</p>

<p>该方法不会阻止事件的进一步传播（stopPropagation方法可用于这个目的）。只要在事件的传播过程中（捕获阶段、目标阶段、冒泡阶段皆可），使用了preventDefault方法，该事件的默认方法就不会执行。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="c1">// HTML代码为</span>
<span class="c1">// &lt;input type="checkbox" id="my-checkbox" /&gt;</span>

<span class="kd">var</span> <span class="nx">cb</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'my-checkbox'</span><span class="p">);</span>

<span class="nx">cb</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span>
  <span class="s1">'click'</span><span class="p">,</span>
  <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">){</span> <span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span> <span class="p">},</span>
  <span class="kc">false</span>
<span class="p">);</span>
</code></pre>
</div>

<p>上面代码为点击单选框的事件，设置监听函数，取消默认行为。由于浏览器的默认行为是选中单选框，所以这段代码会导致无法选中单选框。</p>

<p>利用这个方法，可以为文本输入框设置校验条件。如果用户的输入不符合条件，就无法将字符输入文本框。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">checkName</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">charCode</span> <span class="o">&lt;</span> <span class="mi">97</span> <span class="o">||</span> <span class="nx">e</span><span class="p">.</span><span class="nx">charCode</span> <span class="o">&gt;</span> <span class="mi">122</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre>
</div>

<p>上面函数设为文本框的keypress监听函数后，将只能输入小写字母，否则输入事件的默认事件（写入文本框）将被取消。</p>

<p>如果监听函数最后返回布尔值false（即return false），浏览器也不会触发默认行为，与preventDefault方法有等同效果。</p>

<span id="toc18"></span><h3 id="eventstoppropagation">event.stopPropagation()</h3>

<p><code class="highlighter-rouge">stopPropagation</code>方法阻止事件在DOM中继续传播，防止再触发定义在别的节点上的监听函数，但是不包括在当前节点上新定义的事件监听函数。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">stopEvent</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">e</span><span class="p">.</span><span class="nx">stopPropagation</span><span class="p">();</span>
<span class="p">}</span>

<span class="nx">el</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">stopEvent</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p>将上面函数指定为监听函数，会阻止事件进一步冒泡到el节点的父节点。</p>

<span id="toc19"></span><h3 id="eventstopimmediatepropagation">event.stopImmediatePropagation()</h3>

<p><code class="highlighter-rouge">stopImmediatePropagation</code>方法阻止同一个事件的其他监听函数被调用。</p>

<p>如果同一个节点对于同一个事件指定了多个监听函数，这些函数会根据添加的顺序依次调用。只要其中有一个监听函数调用了stopImmediatePropagation方法，其他的监听函数就不会再执行了。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">l1</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
  <span class="nx">e</span><span class="p">.</span><span class="nx">stopImmediatePropagation</span><span class="p">();</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">l2</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'hello world'</span><span class="p">);</span>
<span class="p">}</span>

<span class="nx">el</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">l1</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
<span class="nx">el</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">l2</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre>
</div>

<p>上面代码在el节点上，为click事件添加了两个监听函数l1和l2。由于l1调用了stopImmediatePropagation方法，所以l2不会被调用。</p>

<span id="toc20"></span><div class="chapter"><h2 id="自定义事件和事件模拟">自定义事件和事件模拟</h2></div>

<p>除了浏览器预定义的那些事件，用户还可以自定义事件，然后手动触发。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="c1">// 新建事件实例</span>
<span class="kd">var</span> <span class="nx">event</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Event</span><span class="p">(</span><span class="s1">'build'</span><span class="p">);</span>

<span class="c1">// 添加监听函数</span>
<span class="nx">elem</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'build'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">},</span> <span class="kc">false</span><span class="p">);</span>

<span class="c1">// 触发事件</span>
<span class="nx">elem</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
</code></pre>
</div>

<p>上面代码触发了自定义事件，该事件会层层向上冒泡。在冒泡过程中，如果有一个元素定义了该事件的监听函数，该监听函数就会触发。</p>

<p>由于IE不支持这个API，如果在IE中自定义事件，需要使用后文的“老式方法”。</p>

<span id="toc21"></span><h3 id="customevent">CustomEvent()</h3>

<p>Event构造函数只能指定事件名，不能在事件上绑定数据。如果需要在触发事件的同时，传入指定的数据，需要使用CustomEvent构造函数生成自定义的事件对象。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">event</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">CustomEvent</span><span class="p">(</span><span class="s1">'build'</span><span class="p">,</span> <span class="p">{</span> <span class="s1">'detail'</span><span class="p">:</span> <span class="s1">'hello'</span> <span class="p">});</span>
<span class="kd">function</span> <span class="nx">eventHandler</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">detail</span><span class="p">);</span>
<span class="p">}</span>
</code></pre>
</div>

<p>上面代码中，CustomEvent构造函数的第一个参数是事件名称，第二个参数是一个对象，该对象的detail属性会绑定在事件对象之上。</p>

<p>下面是另一个例子。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">myEvent</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">CustomEvent</span><span class="p">(</span><span class="s2">"myevent"</span><span class="p">,</span> <span class="p">{</span>
  <span class="na">detail</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">foo</span><span class="p">:</span> <span class="s2">"bar"</span>
  <span class="p">},</span>
  <span class="na">bubbles</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
  <span class="na">cancelable</span><span class="p">:</span> <span class="kc">false</span>
<span class="p">});</span>

<span class="nx">el</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'myevent'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Hello '</span> <span class="o">+</span> <span class="nx">event</span><span class="p">.</span><span class="nx">detail</span><span class="p">.</span><span class="nx">foo</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">el</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">myEvent</span><span class="p">);</span>
</code></pre>
</div>

<p>IE不支持这个方法，可以用下面的垫片函数模拟。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
  <span class="kd">function</span> <span class="nx">CustomEvent</span> <span class="p">(</span> <span class="nx">event</span><span class="p">,</span> <span class="nx">params</span> <span class="p">)</span> <span class="p">{</span>
    <span class="nx">params</span> <span class="o">=</span> <span class="nx">params</span> <span class="o">||</span> <span class="p">{</span> <span class="na">bubbles</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">cancelable</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">detail</span><span class="p">:</span> <span class="kc">undefined</span> <span class="p">};</span>
    <span class="kd">var</span> <span class="nx">evt</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createEvent</span><span class="p">(</span> <span class="s1">'CustomEvent'</span> <span class="p">);</span>
    <span class="nx">evt</span><span class="p">.</span><span class="nx">initCustomEvent</span><span class="p">(</span> <span class="nx">event</span><span class="p">,</span> <span class="nx">params</span><span class="p">.</span><span class="nx">bubbles</span><span class="p">,</span> <span class="nx">params</span><span class="p">.</span><span class="nx">cancelable</span><span class="p">,</span> <span class="nx">params</span><span class="p">.</span><span class="nx">detail</span> <span class="p">);</span>
    <span class="k">return</span> <span class="nx">evt</span><span class="p">;</span>
   <span class="p">}</span>

  <span class="nx">CustomEvent</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">Event</span><span class="p">.</span><span class="nx">prototype</span><span class="p">;</span>

  <span class="nb">window</span><span class="p">.</span><span class="nx">CustomEvent</span> <span class="o">=</span> <span class="nx">CustomEvent</span><span class="p">;</span>
<span class="p">})();</span>
</code></pre>
</div>

<span id="toc22"></span><h3 id="事件的模拟">事件的模拟</h3>

<p>有时，需要在脚本中模拟触发某种类型的事件，这时就必须使用这种事件的构造函数。</p>

<p>下面是一个通过MouseEvent构造函数，模拟触发click鼠标事件的例子。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">simulateClick</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">event</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MouseEvent</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="p">{</span>
    <span class="s1">'bubbles'</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="s1">'cancelable'</span><span class="p">:</span> <span class="kc">true</span>
  <span class="p">});</span>
  <span class="kd">var</span> <span class="nx">cb</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'checkbox'</span><span class="p">);</span>
  <span class="nx">cb</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
<span class="p">}</span>
</code></pre>
</div>

<span id="toc23"></span><h3 id="自定义事件的老式写法">自定义事件的老式写法</h3>

<p>老式浏览器不一定支持各种类型事件的构造函数。因此，有时为了兼容，会用到一些非标准的方法。这些方法未来会被逐步淘汰，但是目前浏览器还广泛支持。除非是为了兼容老式浏览器，尽量不要使用。</p>

<p><strong>（1）document.createEvent()</strong></p>

<p>document.createEvent方法用来新建指定类型的事件。它所生成的Event实例，可以传入dispatchEvent方法。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="c1">// 新建Event实例</span>
<span class="kd">var</span> <span class="nx">event</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createEvent</span><span class="p">(</span><span class="s1">'Event'</span><span class="p">);</span>

<span class="c1">// 事件的初始化</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">initEvent</span><span class="p">(</span><span class="s1">'build'</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>

<span class="c1">// 加上监听函数</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'build'</span><span class="p">,</span> <span class="nx">doSomething</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>

<span class="c1">// 触发事件</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
</code></pre>
</div>

<p>createEvent方法接受一个字符串作为参数，可能的值参见下表“数据类型”一栏。使用了某一种“事件类型”，就必须使用对应的事件初始化方法。</p>

<table>
  <thead>
    <tr>
      <th>事件类型</th>
      <th>事件初始化方法</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>UIEvents</td>
      <td>event.initUIEvent</td>
    </tr>
    <tr>
      <td>MouseEvents</td>
      <td>event.initMouseEvent</td>
    </tr>
    <tr>
      <td>MutationEvents</td>
      <td>event.initMutationEvent</td>
    </tr>
    <tr>
      <td>HTMLEvents</td>
      <td>event.initEvent</td>
    </tr>
    <tr>
      <td>Event</td>
      <td>event.initEvent</td>
    </tr>
    <tr>
      <td>CustomEvent</td>
      <td>event.initCustomEvent</td>
    </tr>
    <tr>
      <td>KeyboardEvent</td>
      <td>event.initKeyEvent</td>
    </tr>
  </tbody>
</table>

<p><strong>（2）event.initEvent()</strong></p>

<p>事件对象的initEvent方法，用来初始化事件对象，还能向事件对象添加属性。该方法的参数必须是一个使用<code class="highlighter-rouge">Document.createEvent()</code>生成的Event实例，而且必须在dispatchEvent方法之前调用。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">event</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createEvent</span><span class="p">(</span><span class="s1">'Event'</span><span class="p">);</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">initEvent</span><span class="p">(</span><span class="s1">'my-custom-event'</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="p">{</span><span class="na">foo</span><span class="p">:</span><span class="s1">'bar'</span><span class="p">});</span>
<span class="nx">someElement</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
</code></pre>
</div>

<p>initEvent方法可以接受四个参数。</p>

<ul>
  <li>type：事件名称，格式为字符串。</li>
  <li>bubbles：事件是否应该冒泡，格式为布尔值。可以使用event.bubbles属性读取它的值。</li>
  <li>cancelable：事件是否能被取消，格式为布尔值。可以使用event.cancelable属性读取它的值。</li>
  <li>option：为事件对象指定额外的属性。</li>
</ul>

<span id="toc24"></span><h3 id="事件模拟的老式写法">事件模拟的老式写法</h3>

<p>事件模拟的非标准做法是，对document.createEvent方法生成的事件对象，使用对应的事件初始化方法进行初始化。比如，click事件对象属于MouseEvent对象，也属于UIEvent对象，因此要用initMouseEvent方法或initUIEvent方法进行初始化。</p>

<p><strong>（1）event.initMouseEvent()</strong></p>

<p>initMouseEvent方法用来初始化Document.createEvent方法新建的鼠标事件。该方法必须在事件新建（document.createEvent方法）之后、触发（dispatchEvent方法）之前调用。</p>

<p>initMouseEvent方法有很长的参数。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">event</span><span class="p">.</span><span class="nx">initMouseEvent</span><span class="p">(</span><span class="nx">type</span><span class="p">,</span> <span class="nx">canBubble</span><span class="p">,</span> <span class="nx">cancelable</span><span class="p">,</span> <span class="nx">view</span><span class="p">,</span>
  <span class="nx">detail</span><span class="p">,</span> <span class="nx">screenX</span><span class="p">,</span> <span class="nx">screenY</span><span class="p">,</span> <span class="nx">clientX</span><span class="p">,</span> <span class="nx">clientY</span><span class="p">,</span>
  <span class="nx">ctrlKey</span><span class="p">,</span> <span class="nx">altKey</span><span class="p">,</span> <span class="nx">shiftKey</span><span class="p">,</span> <span class="nx">metaKey</span><span class="p">,</span>
  <span class="nx">button</span><span class="p">,</span> <span class="nx">relatedTarget</span>
<span class="p">);</span>
</code></pre>
</div>

<p>上面这些参数的含义，参见MouseEvent构造函数的部分。</p>

<p>模仿并触发click事件的写法如下。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">simulateDivClick</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createEvent</span><span class="p">(</span><span class="s1">'MouseEvents'</span><span class="p">);</span>

<span class="nx">simulateDivClick</span><span class="p">.</span><span class="nx">initMouseEvent</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span><span class="kc">true</span><span class="p">,</span><span class="kc">true</span><span class="p">,</span>
  <span class="nb">document</span><span class="p">.</span><span class="nx">defaultView</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="kc">false</span><span class="p">,</span>
  <span class="kc">false</span><span class="p">,</span><span class="kc">false</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="kc">null</span><span class="p">,</span><span class="kc">null</span>
<span class="p">);</span>

<span class="nx">divElement</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">simulateDivClick</span><span class="p">);</span>
</code></pre>
</div>

<p><strong>（2）UIEvent.initUIEvent()</strong></p>

<p><code class="highlighter-rouge">UIEvent.initUIEvent()</code>用来初始化一个UI事件。该方法必须在事件新建（document.createEvent方法）之后、触发（dispatchEvent方法）之前调用。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">event</span><span class="p">.</span><span class="nx">initUIEvent</span><span class="p">(</span><span class="nx">type</span><span class="p">,</span> <span class="nx">canBubble</span><span class="p">,</span> <span class="nx">cancelable</span><span class="p">,</span> <span class="nx">view</span><span class="p">,</span> <span class="nx">detail</span><span class="p">)</span>
</code></pre>
</div>

<p>该方法的参数含义，可以参见MouseEvent构造函数的部分。其中，detail参数是一个数值，含义与事件类型有关，对于鼠标事件，这个值表示鼠标按键在某个位置按下的次数。</p>

<div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">e</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createEvent</span><span class="p">(</span><span class="s2">"UIEvent"</span><span class="p">);</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">initUIEvent</span><span class="p">(</span><span class="s2">"click"</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="nb">window</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</code></pre>
</div>

<span id="toc25"></span><div class="chapter"><h2 id="参考链接" class="reference">参考链接</h2></div>

<ul class="reference-list">
  <li>Wilson Page, <a href="http://coding.smashingmagazine.com/2013/11/12/an-introduction-to-dom-events/">An Introduction To DOM Events</a></li>
  <li>Mozilla Developer Network, <a href="https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching">Using Firefox 1.5 caching</a></li>
  <li>Craig Buckler, <a href="http://www.sitepoint.com/css3-animation-javascript-event-handlers/">How to Capture CSS3 Animation Events in JavaScript</a></li>
  <li>Ray Nicholus, <a href="http://blog.garstasio.com/you-dont-need-jquery/events/">You Don’t Need jQuery!: Events</a></li>
</ul>


</div></div></article>

<div class="row">
<div class="twelve columns">

<h2>留言</h2>

<div id="disqus_thread"></div>
    <script type="text/javascript">
        /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
        var disqus_shortname = 'jstutorial'; // required: replace example with your forum shortname

        /* * * DON'T EDIT BELOW THIS LINE * * */
        (function() {
            var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
            dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
            (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
        })();
    </script>
    <noscript>Please enable JavaScript to view the &lt;a href="http://disqus.com/?ref_noscript"&gt;comments powered by Disqus.&lt;/a&gt;</noscript>
    <a href="http://disqus.com/" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>



</div>
</div>

<footer>
<div class="row">
<div class="twelve columns">
	<p><a href="http://javascript.ruanyifeng.com/introduction/license.html">版权声明</a> | last modified on 2013-12-19 </p>
</div>
</div>
</footer>
<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-43771063-1', 'ruanyifeng.com');
  ga('send', 'pageview');
</script>




</body></html>